package com.letoo.dragon.common.utils;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

/**
 * 数学工具类。涉及到数字、随机数等等操作函数。
 * 
 * @author dengjie
 *
 */
public class MathUtils {
    /**
     * 功能：将字符串转换为BigDecimal，一般用于数字运算时。
     * 
     * @param str
     *            字符串
     * @return BigDecimal,str为empty时返回null。
     */
    public static BigDecimal toBigDecimal(String str) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        return new BigDecimal(str);
    }

    /**
     * 功能：将字符串抓换为double，如果失败返回默认值。
     * 
     * @param str
     *            字符串
     * @param defaultValue
     *            失败时返回的默认值
     * @return double
     */
    public static double toDouble(String str, double defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Double.parseDouble(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * 功能：将字符串抓换为float，如果失败返回默认值。
     * 
     * @param str
     *            字符串
     * @param defaultValue
     *            失败时返回的默认值
     * @return float
     */
    public static float toFloat(String str, float defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Float.parseFloat(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * 功能：将字符串抓换为long，如果失败返回默认值。
     * 
     * @param str
     *            字符串
     * @param defaultValue
     *            失败时返回的默认值
     * @return long
     */
    public static long toLong(String str, long defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Long.parseLong(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * 功能：将字符串抓换为int，如果失败返回默认值。
     * 
     * @param str
     *            字符串
     * @param defaultValue
     *            失败时返回的默认值
     * @return int
     */
    public static int toInt(String str, int defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * <p>
     * 得到两个 <code>double</code>值中最大的一个.
     * </p>
     * 
     * @param a
     *            值 1
     * @param b
     *            值 2
     * @return 最大的值
     */
    public static float getMax(float a, float b) {
        if (Float.isNaN(a)) {
            return b;
        } else if (Float.isNaN(b)) {
            return a;
        } else {
            return Math.max(a, b);
        }
    }

    /**
     * <p>
     * 得到数组中最大的一个.
     * </p>
     * 
     * @param array
     *            数组不能为null，也不能为空。
     * @return 得到数组中最大的一个.
     * @throws IllegalArgumentException
     *             如果 <code>数组</code> 是 <code>null</code>
     * @throws IllegalArgumentException
     *             如果 <code>数组</code>是空
     */
    public static float getMax(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        float max = array[0];
        for (int j = 1; j < array.length; j++) {
            max = getMax(array[j], max);
        }

        return max;
    }

    /**
     * <p>
     * 得到数组中最大的一个.
     * </p>
     * 
     * @param array
     *            数组不能为null，也不能为空。
     * @return 得到数组中最大的一个.
     * @throws IllegalArgumentException
     *             如果 <code>数组</code> 是 <code>null</code>
     * @throws IllegalArgumentException
     *             如果 <code>数组</code>是空
     */
    public static double getMax(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        double max = array[0];
        for (int j = 1; j < array.length; j++) {
            max = getMax(array[j], max);
        }

        return max;
    }

    /**
     * <p>
     * 得到两个 <code>double</code>值中最大的一个.
     * </p>
     * 
     * @param a
     *            值 1
     * @param b
     *            值 2
     * @return 最大的值
     * */
    public static double getMax(double a, double b) {
        if (Double.isNaN(a)) {
            return b;
        } else if (Double.isNaN(b)) {
            return a;
        } else {
            return Math.max(a, b);
        }
    }

    /**
     * <p>
     * 得到两个float中最小的一个。
     * </p>
     * 
     * @param a
     *            值 1
     * @param b
     *            值 2
     * @return double值最小的
     */
    public static float getMin(float a, float b) {
        if (Float.isNaN(a)) {
            return b;
        } else if (Float.isNaN(b)) {
            return a;
        } else {
            return Math.min(a, b);
        }
    }

    /**
     * <p>
     * 返回数组中最小的数值。
     * </p>
     * 
     * @param array
     *            数组不能为null，也不能为空。
     * @return 数组里面最小的float
     * @throws IllegalArgumentException
     *             如果<code>数组</code>是<code>null</code>
     * @throws IllegalArgumentException
     *             如果<code>数组</code>是空
     */
    public static float getMin(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("数组不能为null。");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("数组不能为空。");
        }

        // Finds and returns min
        float min = array[0];
        for (int i = 1; i < array.length; i++) {
            min = getMin(array[i], min);
        }

        return min;
    }

    /**
     * <p>
     * 返回数组中最小的double。
     * </p>
     * 
     * @param array
     *            数组不能为null，也不能为空。
     * @return 数组里面最小的double
     * @throws IllegalArgumentException
     *             如果<code>数组</code>是<code>null</code>
     * @throws IllegalArgumentException
     *             如果<code>数组</code>是空
     */
    public static double getMin(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("数组不能为null。");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("数组不能为空。");
        }
        // Finds and returns min
        double min = array[0];
        for (int i = 1; i < array.length; i++) {
            min = getMin(array[i], min);
        }
        return min;
    }

    /**
     * <p>
     * 得到两个double中最小的一个。
     * </p>
     * 
     * @param a
     *            值 1
     * @param b
     *            值 2
     * @return double值最小的
     */
    public static double getMin(double a, double b) {
        if (Double.isNaN(a)) {
            return b;
        } else if (Double.isNaN(b)) {
            return a;
        } else {
            return Math.min(a, b);
        }
    }

    /**
     * 返回两个double的商 first除以second。
     * 
     * @param first
     *            第一个double
     * @param second
     *            第二个double
     * @return double
     */
    public static double divideDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.divide(b2).doubleValue();
    }

    /**
     * 返回两个double的乘积 first*second。
     * 
     * @param first
     *            第一个double
     * @param second
     *            第二个double
     * @return double
     */
    public static double multiplyDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 返回两个double的差值 first-second。
     * 
     * @param first
     *            第一个double
     * @param second
     *            第二个double
     * @return double
     */
    public static double subtractDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 返回两个double的和值 first+second。
     * 
     * @param first
     *            第一个double
     * @param second
     *            第二个double
     * @return double
     */
    public static double sumDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.add(b2).doubleValue();
    }

    /**
     * 格式化double指定位数小数。例如将11.123格式化为11.1。
     * 
     * @param value
     *            原double数字。
     * @param decimals
     *            小数位数。
     * @return 格式化后的double，注意为硬格式化不存在四舍五入。
     */
    public static String formatDouble(double value, int decimals) {
        String doubleStr = "" + value;
        int index = doubleStr.indexOf(".") != -1 ? doubleStr.indexOf(".") : doubleStr.indexOf(",");
        // Decimal point can not be found...
        if (index == -1)
            return doubleStr;
        // Truncate all decimals
        if (decimals == 0) {
            return doubleStr.substring(0, index);
        }
        int len = index + decimals + 1;
        if (len >= doubleStr.length())
            len = doubleStr.length();
        double d = Double.parseDouble(doubleStr.substring(0, len));
        return String.valueOf(d);
    }

    /**
     * 生成一个指定位数的随机数，并将其转换为字符串作为函数的返回值。
     * 
     * @param numberLength
     *            随机数的位数。
     * @return String 注意随机数可能以0开头。
     */
    public static String randomNumber(int numberLength) {
        // 记录生成的每一位随机数
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < numberLength; i++) {
            // 每次生成一位,随机生成一个0-10之间的随机数,不含10。
            Double ranDouble = Math.floor(Math.random() * 10);
            sb.append(ranDouble.intValue());
        }
        return sb.toString();
    }

    /**
     * 功能：生成一个在最大数和最小数之间的随机数。会出现最小数，但不会出现最大数。
     * 
     * @param minNum
     *            最小数
     * @param maxNum
     *            最大数
     * @return int
     */
    public static int randomNumber(int minNum, int maxNum) {
        if (maxNum <= minNum) {
            throw new RuntimeException("maxNum必须大于minNum!");
        }
        // 计算出来差值
        int subtract = maxNum - minNum;
        Double ranDouble = Math.floor(Math.random() * subtract);
        return ranDouble.intValue() + minNum;
    }

    /**
     * 功能：生成一个在最大数和最小数之间的随机数。会出现最小数，但不会出现最大数。<br/>
     * 但不随机notin数组中指定的数字， 如果可随机的范围较小，可能会一直随机不到，或者随机的很慢。
     * 
     * @param minNum
     *            最小数
     * @param maxNum
     *            最大数
     * @param notin
     *            不随机数组这些数字
     * @return int
     */
    public static int randomNumber(int minNum, int maxNum, List<Integer> notin) {
        if (notin.size() >= (maxNum - minNum)) {
            throw new RuntimeException("notin数组的元素已经把可以随机的都排除了，无法得到随机数!");
        }
        while (true) {
            int num = randomNumber(minNum, maxNum);
            if (!notin.contains(num)) {
                return num;
            }
        }
    }

    /**
     * 生成不重复随机数 根据给定的最小数字和最大数字，以及随机数的个数，产生指定的不重复的数组
     * 
     * @param begin
     *            最小数字（包含该数）
     * @param end
     *            最大数字（不包含该数）
     * @param size
     *            指定产生随机数的个数
     */
    public int[] generateRandomNumber(int begin, int end, int size) {
        // 加入逻辑判断，确保begin<end并且size不能大于该表示范围
        if (begin >= end || (end - begin) < size) {
            return null;
        }
        // 种子你可以随意生成，但不能重复
        int[] seed = new int[end - begin];

        for (int i = begin; i < end; i++) {
            seed[i - begin] = i;
        }
        int[] ranArr = new int[size];
        Random ran = new Random();
        // 数量你可以自己定义。
        for (int i = 0; i < size; i++) {
            // 得到一个位置
            int j = ran.nextInt(seed.length - i);
            // 得到那个位置的数值
            ranArr[i] = seed[j];
            // 将最后一个未用的数字放到这里
            seed[j] = seed[seed.length - 1 - i];
        }
        return ranArr;
    }

    /**
     * 生成不重复随机数 根据给定的最小数字和最大数字，以及随机数的个数，产生指定的不重复的数组
     * 
     * @param begin
     *            最小数字（包含该数）
     * @param end
     *            最大数字（不包含该数）
     * @param size
     *            指定产生随机数的个数
     */
    public Integer[] generateBySet(int begin, int end, int size) {
        // 加入逻辑判断，确保begin<end并且size不能大于该表示范围
        if (begin >= end || (end - begin) < size) {
            return null;
        }

        Random ran = new Random();
        Set<Integer> set = new HashSet<Integer>();
        while (set.size() < size) {
            set.add(begin + ran.nextInt(end - begin));
        }

        Integer[] ranArr = new Integer[size];
        ranArr = set.toArray(new Integer[size]);
        // ranArr = (Integer[]) set.toArray();

        return ranArr;
    }

    /**
     * 判断String是否是整数
     */
    public boolean isInteger(String s) {
        if ((s != null) && (s != ""))
            return s.matches("^[0-9]*$");
        else
            return false;
    }

    /**
     * 判断字符串是否是浮点数
     */
    public boolean isDouble(String value) {
        try {
            Double.parseDouble(value);
            if (value.contains("."))
                return true;
            return false;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 判断字符串是否是数字
     */
    public boolean isNumber(String value) {
        return isInteger(value) || isDouble(value);
    }

    // 排序方法
    public static void sort(int[] array) {// 小到大的排序
        int temp = 0;
        for (int i = 0; i < array.length; i++) {
            for (int j = i; j < array.length; j++) {
                if (array[i] > array[j]) {
                    temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
    }

    /**
     * 是否是质数
     */
    public static boolean isPrimes(int n) {
        for (int i = 2; i <= Math.sqrt(n); i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 阶乘
     * 
     * @param n
     * @return
     */
    public static int factorial(int n) {
        if (n == 1) {
            return 1;
        }
        return n * factorial(n - 1);
    }

    /**
     * 平方根算法
     * 
     * @param x
     * @return
     */
    public static long sqrt(long x) {
        long y = 0;
        long b = (~Long.MAX_VALUE) >>> 1;
        while (b > 0) {
            if (x >= y + b) {
                x -= y + b;
                y >>= 1;
                y += b;
            } else {
                y >>= 1;
            }
            b >>= 2;
        }
        return y;
    }

    private int math_subnode(int selectNum, int minNum) {
        if (selectNum == minNum) {
            return 1;
        } else {
            return selectNum * math_subnode(selectNum - 1, minNum);
        }
    }

    private int math_node(int selectNum) {
        if (selectNum == 0) {
            return 1;
        } else {
            return selectNum * math_node(selectNum - 1);
        }
    }

    /**
     * 可以用于计算双色球、大乐透注数的方法 selectNum：选中了的小球个数 minNum：至少要选中多少个小球 比如大乐透35选5可以这样调用processMultiple(7,5); 就是数学中的：C75=7*6/2*1
     */
    public int processMultiple(int selectNum, int minNum) {
        int result;
        result = math_subnode(selectNum, minNum) / math_node(selectNum - minNum);
        return result;
    }

    /**
     * 求m和n的最大公约数
     */
    public static int gongyue(int m, int n) {
        while (m % n != 0) {
            int temp = m % n;
            m = n;
            n = temp;
        }
        return n;
    }

    /**
     * 求两数的最小公倍数
     */
    public static int gongbei(int m, int n) {
        return m * n / gongyue(m, n);
    }

    /**
     * 递归求两数的最大公约数
     */
    public static int divisor(int m, int n) {
        if (m % n == 0) {
            return n;
        } else {
            return divisor(n, m % n);
        }
    }

    /**
     * 返回长度为strLength的随机数，在前面补0
     * 
     * @param strLength 指定长度
     * @return
     */
    public static String getRandowNumber(int strLength) {
        Random rm = new Random();
        // 获得随机数
        double pross = (1 + rm.nextDouble()) * Math.pow(10, strLength);
        // 将获得的获得随机数转化为字符串
        String fixLenthString = String.valueOf(pross);
        // 返回固定的长度的随机数
        return fixLenthString.substring(1, strLength + 1);
    }
    
}
